﻿#define DEBUG

using Microsoft.AnalysisServices;
using Microsoft.AnalysisServices.AdomdClient;
using Microsoft.Practices.Unity;
using Moop.Common;
using Moop.Common.IFaces;
using Moop.DataLayer.Olap.Impl;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace Moop.DataLayer.Olap.Service
{
    public class OlapMetaDataService : IOlapMetaDataService
    {

        #region IOlapMetaDataService Members

        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }

        [Dependency]
        public IOlapClient OlapClient { get; set; }
        [Dependency]
        public IOlapServer OlapServer { get; set; }
        [Dependency]
        public IOlapDatabase OlapDatabase { get; set; }

        #endregion


        public OlapMetaDataService()
        {
        }


        public OlapMetaDataService(string connectionString)
        {
            ConnectionString = connectionString;
            Init();
        }


        public void Init()
        {
            OlapClient.ConnectionString = ConnectionString;
            OlapClient.ConnectionString = OlapClient.ConnectionString;

            OlapServer.ConnectionString = ConnectionString;
            OlapServer.Server.Connect(ConnectionString);
        }


        public IOlapDatabase Populate()
        {
            Database database = OlapServer.Server.Databases[DatabaseName];
            Parallel.ForEach(database.Cubes.Cast<Microsoft.AnalysisServices.Cube>(), cube => GetCubeMetaData(cube));
            return OlapDatabase;
        }


        private void GetCubeMetaData(Microsoft.AnalysisServices.Cube dbCube)
        {

#if DEBUG
            if (dbCube.Name != "Adventure Works") { return; }
#endif

            using (AdomdConnection conn = OlapClient.GetConnection())
            {
                if (conn.State != System.Data.ConnectionState.Open) { conn.Open(); }

                ICube cube = TypeContainer.Instance.Resolve<ICube>();
                cube.Name = dbCube.Name;

                #region MeasureGroups

                foreach (MeasureGroup measureGroup in dbCube.MeasureGroups)
                {
                    var cubeMeasureGroup = TypeContainer.Instance.Resolve<ICubeMeasureGroup>();

                    #region Get MeasureGroup Dimensions

                    IEnumerable<MeasureGroupDimension> mgDims = measureGroup.Dimensions.Cast<MeasureGroupDimension>();
#if DEBUG
                    mgDims = new List<MeasureGroupDimension>(measureGroup.Dimensions.Cast<MeasureGroupDimension>().Where(p => p.Dimension.Name == "Account"));
#endif

                    foreach (MeasureGroupDimension dim in mgDims)
                    {
                        #region Set Dimension

                        var cubeDim = TypeContainer.Instance.Resolve<ICubeDimension>();
                        cubeDim.Name = dim.CubeDimension.Name;
                        cubeDim.UniqueName = dim.CubeDimension.Name;
                        cubeDim.Type = dim.CubeDimension.Dimension.Type;

                        #endregion


                        #region Dim NamedSets (get via OlapClient only it seems)

                        var DimNamedSet = conn.Cubes[dbCube.Name].NamedSets
                                              .Cast<NamedSet>()
                                              .Where(p => p.Properties["DIMENSIONS"].Value
                                              .ToString().Contains(cubeDim.Name));

                        foreach (var item in DimNamedSet)
                        {
                            var cubeDimSet = TypeContainer.Instance.Resolve<ICubeDimensionSet>();
                            cubeDimSet.Name = item.Name; cubeDimSet.DisplayFolder = item.DisplayFolder; cubeDimSet.OlapPath = item.Name;
                            cubeDim.Sets.Add(cubeDimSet);
                        }

                        cubeDim.Sets = new ConcurrentBag<ICubeDimensionSet>(cubeDim.Sets.OrderBy(p => p.Name));

                        #endregion


                        #region Dim Attributes

                        foreach (DimensionAttribute attr in dim.Dimension.Attributes)
                        {
                            if (attr.AttributeHierarchyEnabled == true)
                            {
                                var cubeDimAttr = TypeContainer.Instance.Resolve<ICubeDimensionAttribute>();
                                cubeDimAttr.DisplayFolder = attr.AttributeHierarchyDisplayFolder;
                                cubeDimAttr.Name = SetAttrName(cubeDim, attr, cubeDimAttr);
                                cubeDimAttr.OlapPath = cubeDim.Name.PadWithSquareNotLast() + attr.Name.PadWithSquare();
                                cubeDim.Attributes.Add(cubeDimAttr);
                            }
                        }

                        cubeDim.Attributes = new ConcurrentBag<ICubeDimensionAttribute>(cubeDim.Attributes.OrderByDescending(p => p.Name));

                        #endregion


                        #region Dim Hierarchies

                        IEnumerable<ICubeDimensionHierarchy> hierarchies = GetHierarchies(dbCube.Name, dim.Dimension.Name.PadWithSquare());

                        foreach (ICubeDimensionHierarchy hierar in hierarchies)
                        {
                            var cubeDimensionHierarchy = TypeContainer.Instance.Resolve<ICubeDimensionHierarchy>();
                            cubeDimensionHierarchy.Name = hierar.Name.LastOlapMemberInPath();
                            cubeDimensionHierarchy.DisplayFolder = hierar.DisplayFolder;
                            cubeDimensionHierarchy.OlapPath = hierar.Name;

                            #region Hierarchies Levels

                            IEnumerable<ICubeDimensionHierarchyLevel> levels = GetHierarchyLevels(dbCube.Name, dim.Dimension.Name.PadWithSquare(), cubeDimensionHierarchy.OlapPath);
                            if (levels != null && levels.Count() > 0)
                            {
                                foreach (CubeDimensionHierarchyLevel level in levels)
                                {
                                    var hierLevel = TypeContainer.Instance.Resolve<ICubeDimensionHierarchyLevel>();
                                    hierLevel.Name = level.Name.LastOlapMemberInPath();
                                    hierLevel.OlapPath = level.OlapPath;
                                    cubeDimensionHierarchy.Levels.Add(hierLevel);
                                }
                            }
                            cubeDimensionHierarchy.Levels = new ConcurrentBag<ICubeDimensionHierarchyLevel>(cubeDimensionHierarchy.Levels.OrderBy(p => p.Name));

                            #endregion

                            cubeDim.Hierarchies.Add(cubeDimensionHierarchy);
                        }

                        cubeDim.Hierarchies = new ConcurrentBag<ICubeDimensionHierarchy>(cubeDim.Hierarchies.OrderBy(p => p.Name));

                        #endregion


                        #region Add to MeasurGroup and Cube

                        //Add Dim to MeasureGroup (Name Only)
                        cubeMeasureGroup.Dimensions.Add(cubeDim.Name);

                        //Add Dim to Cube (Complete Object)
                        if (!cube.CubeDimensions.ContainsKey(cubeDim.Name))
                        {
                            cube.CubeDimensions.TryAdd(cubeDim.Name, cubeDim);
                        }

                        #endregion

                    }
                    #endregion

                    //Add measureGroup to Cube
                    cubeMeasureGroup.Name = measureGroup.Name;
                    cube.MeasureGroups.Add(measureGroup.Name, cubeMeasureGroup);
                }
                #endregion

                //Add Cube to Cube Collection
                OlapDatabase.Name = DatabaseName;
                OlapDatabase.Cubes.Add(dbCube.Name, cube);
            }
        }


        private string SetAttrName(ICubeDimension cubeDim, DimensionAttribute attr, ICubeDimensionAttribute cubeDimAttr)
        {
            if (cubeDim.Type == DimensionType.Time)
                cubeDimAttr.Name = cubeDim.Name + ExtUtils.DOT + attr.Name;
            else
                cubeDimAttr.Name = attr.Name;

            return cubeDimAttr.Name;
        }



        public List<ICubeDimensionHierarchy> GetHierarchies(string cubeName, string dimensionUniqueName)
        {
            List<ICubeDimensionHierarchy> uniqueHier = null;

            using (AdomdConnection conn = OlapClient.GetConnection())
            {
                #region Set Restrictions
                AdomdRestrictionCollection restrictions = new AdomdRestrictionCollection();
                restrictions.Add(CONSTS.CATALOG_NAME, conn.Database);
                restrictions.Add(CONSTS.CUBE_NAME, cubeName);
                restrictions.Add(CONSTS.DIMENSION_UNIQUE_NAME, dimensionUniqueName);
                #endregion

                DataSet ds = conn.GetSchemaDataSet(CONSTS.MDSCHEMA_PROPERTIES, restrictions);

                if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    DataTable table = ds.Tables[0];
                    if (ds.Tables[0].Columns.Count > 0)
                    {
                        var levs = table.AsEnumerable()
                                        .GroupBy(g => new Hierarchy
                                                                {
                                                                    HierarchyName = g.Field<string>("HIERARCHY_UNIQUE_NAME"),
                                                                    HierarchyLevelName = g.Field<string>("LEVEL_UNIQUE_NAME")
                                                                }
                                                            )
                                        .Select(s => new Hierarchy
                                                                {
                                                                    HierarchyName = s.Key.HierarchyName,
                                                                    HierarchyLevelName = s.Key.HierarchyLevelName
                                                                }
                                                                )
                                        .DistinctBy(p => new  { p.HierarchyName, p.HierarchyLevelName });

                        var sampleLev = new ConcurrentBag<ICubeDimensionHierarchyLevel>
                                        (levs.Where(p => p.HierarchyName == "[Account].[Accounts]")
                                             .Select(item => new CubeDimensionHierarchyLevel() { Name = item.HierarchyLevelName, HierarchyUniqueName = item.HierarchyName })
                                         );


                        int j = 3;
                        uniqueHier = table.DefaultView
                                            .ToTable(true, CONSTS.HIERARCHY_UNIQUE_NAME).AsEnumerable()
                                            .Select(p =>
                                                new CubeDimensionHierarchy()
                                                {
                                                    Name = p.Field<string>(CONSTS.HIERARCHY_UNIQUE_NAME),
                                                    OlapPath = p.Field<string>(CONSTS.HIERARCHY_UNIQUE_NAME)
                                                    ,
                                                    Levels = new ConcurrentBag<ICubeDimensionHierarchyLevel>
                                                        (levs.Where(lp => lp.HierarchyName == p.Field<string>(CONSTS.HIERARCHY_UNIQUE_NAME))
                                                        .Select(item => new CubeDimensionHierarchyLevel() { Name = item.HierarchyLevelName, HierarchyUniqueName = item.HierarchyName })
                                                        )
                                                })
                                            .ToList<ICubeDimensionHierarchy>();

                        int k = 9;
                    }
                }
            }
            return uniqueHier;
        }



        public List<ICubeDimensionHierarchyLevel> GetHierarchyLevels(string cubeName, string dimensionUniqueName, string hierarchyUniqueName)
        {
            List<ICubeDimensionHierarchyLevel> uniqueLevels = null;

            using (AdomdConnection conn = OlapClient.GetConnection())
            {
                #region Set Restrictions
                AdomdRestrictionCollection restrictions = new AdomdRestrictionCollection();
                restrictions.Add(CONSTS.CATALOG_NAME, conn.Database);
                restrictions.Add(CONSTS.CUBE_NAME, cubeName);
                restrictions.Add(CONSTS.DIMENSION_UNIQUE_NAME, dimensionUniqueName);
                restrictions.Add(CONSTS.HIERARCHY_UNIQUE_NAME, hierarchyUniqueName);
                #endregion

                DataSet ds = conn.GetSchemaDataSet(CONSTS.MDSCHEMA_PROPERTIES, restrictions);

                if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    DataTable table = ds.Tables[0];
                    if (ds.Tables[0].Columns.Count > 0)
                    {
                        uniqueLevels = table.DefaultView
                                            .ToTable(true, CONSTS.LEVEL_UNIQUE_NAME).AsEnumerable()
                                            .Select(p => new CubeDimensionHierarchyLevel() { Name = p.Field<string>(CONSTS.LEVEL_UNIQUE_NAME).LastOlapMemberInPath(), OlapPath = p.Field<string>(CONSTS.LEVEL_UNIQUE_NAME) })
                                            .ToList<ICubeDimensionHierarchyLevel>();
                    }
                }
            }
            return uniqueLevels;
        }



    }

    class Hierarchy
    {
        public string HierarchyName { get; set; }
        public string HierarchyLevelName { get; set; }
    }
}
